package com.mx.recycleview.recycleview

import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.*
import android.widget.FrameLayout
import android.widget.RadioButton
import android.widget.Space
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import com.mx.recycleview.R
import com.mx.recycleview.base.BaseSimpleAdapt
import com.mx.recycleview.base.BaseViewHolder
import kotlinx.android.synthetic.main.layout_mx_banner_view.view.*

/**
 * 自定义横向滚动的RecycleView
 * 特性：
 *  1：支持自定义原点样式、位置，框架只提供基础的RadioGroup+RadioButton，其余所有样式都要自己写
 *  2：支持循环轮播
 */
class MXBannerView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    private var autoDiffTime: Int = 5 //轮播模式下的时间间隔
    private var isLoopShow: Boolean = false // 循环显示
    private var isAutoPlay: Boolean = false // 自动轮播
    private var radioButtonBuild: ((RadioButton) -> Unit)? = null // 指示器构建
    private val linearLayoutManager =
        LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
    private val mHandler = Handler(Looper.getMainLooper())
    private val pagerSnapHelper = PagerSnapHelper()

    init {
        LayoutInflater.from(context).inflate(R.layout.layout_mx_banner_view, this, true)
        recycleView.layoutManager = linearLayoutManager
        recycleView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            private var preIndex: Int? = null
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    val size = myAdapt?.itemCount ?: return
                    val index = linearLayoutManager.findFirstCompletelyVisibleItemPosition() % size
                    if (preIndex == index || index < 0) return
                    preIndex = index
                    selectIndex = index
                    if (radioButtonBuild != null) {
                        radioGroup.findViewWithTag<RadioButton>(index)?.performClick()
                    }
                    selectCall?.invoke(index)
                }
            }
        })
    }

    private var selectIndex = 0 // 当前选中的Index
    private var selectCall: ((Int) -> Unit)? = null // 停止滚动时监听回调

    /**
     * 选中监听
     */
    fun setOnSelectListener(select: ((Int) -> Unit)?) {
        selectCall = select
    }

    fun getCurrentSelect() = selectIndex

    /**
     * 设置指示器位置、样式
     * 如果没有调用这个方法，位置指示器将不会显示
     * @param build
     *      RadioButton初始化回调，这里回调后可以设置对应样式
     * @param gravity
     *      指示器在FrameLayout对应的位置
     * @param groupMargin
     *      指示器对应的外边距
     */
    fun setIndicateGravity(
        build: ((RadioButton) -> Unit),
        gravity: Int = Gravity.RIGHT,
        groupMargin: Int? = null
    ) {
        (radioGroup.layoutParams as LayoutParams).gravity = gravity
        groupMargin?.let { margin ->
            (radioGroup.layoutParams as LayoutParams?)?.setMargins(
                margin, margin, margin, margin
            )
        }
        radioGroup.visibility = View.VISIBLE
        radioButtonBuild = build
    }

    /**
     * 适配器设置
     */
    fun setAdapt(adapt: BaseSimpleAdapt<*>) {
        myAdapt?.unregisterAdapterDataObserver(dataObserver)
        myAdapt = adapt
        adapt.registerAdapterDataObserver(dataObserver)
        recycleView.adapter = bannerAdapt
        if (isLoopShow) {
            smoothScrollerToFirst()
        }

        resetIndicator()
    }

    private var hasScrollToFirst = false
    private fun checkFirstScroll() {
        if (!hasScrollToFirst) {
            smoothScrollerToFirst()
        }
    }

    /**
     * 缓慢滚动到第一个View
     */
    private fun smoothScrollerToFirst() {
        val size = myAdapt?.itemCount ?: 0
        if (size <= 0) {
            hasScrollToFirst = false
            return
        }
        if (isLoopShow) {
            val index = myAdapt?.itemCount?.times(1000) ?: return
            recycleView.scrollToPosition(index - 1)
            recycleView.smoothScrollToPosition(index)
        } else {
            recycleView.scrollToPosition(0)
        }
        pagerSnapHelper.attachToRecyclerView(null)
        pagerSnapHelper.attachToRecyclerView(recycleView)
        hasScrollToFirst = true
    }

    fun setLoop(loop: Boolean) {
        isLoopShow = loop
        smoothScrollerToFirst()
    }

    /**
     * 重置指示器
     */
    private fun resetIndicator() {
        val builder = radioButtonBuild ?: return
        val size = myAdapt?.itemCount
        if (size == null || size <= 0) {
            radioGroup.removeAllViews()
            return
        }
        if (radioGroup.childCount > size) {
            val delete = radioGroup.childCount - size
            repeat(delete) { radioGroup.removeView(radioGroup.getChildAt(0)) }
        } else if (radioGroup.childCount < size) {
            val add = size - radioGroup.childCount
            repeat(add) {
                val radio = RadioButton(context)
                radioGroup.addView(radio)
                radio.buttonDrawable = null
                builder.invoke(radio)
            }
        }
        (0..radioGroup.childCount).mapNotNull { radioGroup.getChildAt(it) }.forEachIndexed { index, view ->
            view.tag = index
        }
    }

    private val dataObserver = object : RecyclerView.AdapterDataObserver() {
        override fun onChanged() {
            resetIndicator()
            checkFirstScroll()
            bannerAdapt.notifyDataSetChanged()
        }

        override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
            resetIndicator()
            checkFirstScroll()
            bannerAdapt.notifyItemRangeRemoved(positionStart, itemCount)
        }

        override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
            resetIndicator()
            checkFirstScroll()
            bannerAdapt.notifyItemRangeInserted(positionStart, itemCount)
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
            super.onItemRangeChanged(positionStart, itemCount)
            bannerAdapt.notifyItemRangeChanged(positionStart, itemCount)
        }

        override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
            super.onItemRangeChanged(positionStart, itemCount, payload)
            bannerAdapt.notifyItemRangeChanged(positionStart, itemCount, payload)
        }

        override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
            super.onItemRangeMoved(fromPosition, toPosition, itemCount)
            bannerAdapt.notifyItemMoved(fromPosition, toPosition)
        }
    }

    /**
     * 开始轮播,轮播模式强制开启循环
     * @param duration 间隔：秒
     */
    fun startAutoPlay(duration: Int) {
        autoDiffTime = duration
        isAutoPlay = true
        isLoopShow = true
        smoothScrollerToFirst()
        mHandler.postDelayed(autoPlayRun, duration * 1000L)
    }

    fun stopAutoPlay() {
        isAutoPlay = false
        mHandler.removeCallbacksAndMessages(null)
    }

    /**
     * 监听触摸，触摸按下时停止自动滚动，抬起时开始继续滚动
     */
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        if (isLoopShow && isAutoPlay) {
            when (ev?.action) {
                MotionEvent.ACTION_DOWN -> {
                    mHandler.removeCallbacksAndMessages(null)
                }
                MotionEvent.ACTION_UP, MotionEvent.ACTION_OUTSIDE -> {
                    mHandler.postDelayed(autoPlayRun, autoDiffTime * 1000L)
                }
                else -> {
                }
            }
        }
        return super.dispatchTouchEvent(ev)
    }

    private val autoPlayRun = object : Runnable {
        override fun run() {
            if (!isLoopShow || !isAutoPlay) return
            try {
                val index = linearLayoutManager.findLastCompletelyVisibleItemPosition() + 1
                recycleView.smoothScrollToPosition(index)
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                mHandler.removeCallbacksAndMessages(null)
                mHandler.postDelayed(this, autoDiffTime * 1000L)
            }
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (isLoopShow && isAutoPlay) {
            mHandler.removeCallbacksAndMessages(null)
            mHandler.postDelayed(autoPlayRun, autoDiffTime * 1000L)
        }
    }

    override fun onDetachedFromWindow() {
        mHandler.removeCallbacksAndMessages(null)
        super.onDetachedFromWindow()
    }

    private var myAdapt: BaseSimpleAdapt<*>? = null
    private val bannerAdapt = object : RecyclerView.Adapter<BaseViewHolder>() {
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
            return myAdapt?.onCreateViewHolder(parent, viewType)
                ?: BaseViewHolder(Space(parent.context), viewType)
        }

        override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
            val size = myAdapt?.itemCount ?: 0
            val index = position % size
            myAdapt?.onBindViewHolder(holder, index)
        }

        override fun getItemCount(): Int {
            val size = myAdapt?.itemCount ?: 0
            if (isLoopShow && size > 1) {
                return Int.MAX_VALUE
            }
            return size
        }
    }
}